<!DOCTYPE html>
<!--
Copyright 2020 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel="import" href="/tracing/core/test_utils.html">
<link rel="import" href="/tracing/metrics/custom_metric.html">
<link rel="import" href="/tracing/value/histogram_set.html">

<script>
'use strict';

tr.b.unittest.testSuite(function() {
  test('customMetric_collectTrace', function() {
    const model = tr.c.TestUtils.newModel((model) => {
      const browserThread = setUpBrowserThread(model);
      addTraces(browserThread);
      addSingleTrace(setUpOtherBrowserThread(model));
      addSingleTrace(setUpOtherRendererThread(model));
      const rendererMainThread = setUpRendererMainThread(model);
      setMetrics(rendererMainThread);
    });
    const histograms = new tr.v.HistogramSet();
    tr.metrics.customMetric(histograms, model);
    const hist1 = histograms.getHistogramNamed('metric1');
    assert.strictEqual(hist1.numValues, 2);
    assert.strictEqual(hist1.unit.asJSON(), 'ms_smallerIsBetter');
    const hist2 = histograms.getHistogramNamed('metric2');
    assert.strictEqual(hist2.numValues, 1);
    assert.strictEqual(hist2.unit.asJSON(), 'ms_biggerIsBetter');
    assert.strictEqual(hist2.description, 'This is metric2');
    const hist3 = histograms.getHistogramNamed('metric3');
    // metric3 is not collected because its name isn't reported.
    assert.isUndefined(hist3);
    const hist4 = histograms.getHistogramNamed('metric4');
    assert.strictEqual(hist4.numValues, 2);
    // metric5 is collected from both other browser thread and
    // other renderer thread.
    const hist5 = histograms.getHistogramNamed('metric5');
    assert.strictEqual(hist5.numValues, 2);
  });

  test('customMetric_collectPerformanceMark', function() {
    const model = tr.c.TestUtils.newModel((model) => {
      const rendererMainThread = setUpRendererMainThread(model);
      addPerformanceMark(rendererMainThread);
    });
    const histograms = new tr.v.HistogramSet();
    tr.metrics.customMetric(histograms, model);
    const hist1 = histograms.getHistogramNamed('metric1');
    assert.strictEqual(hist1.numValues, 2);
    assert.strictEqual(hist1.max, 5.0);
    assert.strictEqual(hist1.min, 2.0);
    assert.strictEqual(hist1.average, 3.5);
    const hist2 = histograms.getHistogramNamed('metric2');
    assert.strictEqual(hist2.numValues, 1);
    assert.strictEqual(hist2.max, 18.0);
    assert.strictEqual(hist2.min, 18.0);
    assert.strictEqual(hist2.average, 18.0);
    // metric3 is not collected because
    // ":metric_begin" and ":metric_end" are missing.
    assert.isUndefined(histograms.getHistogramNamed('metric3'));
    // metric4 is not collected because
    // ":metric_end" is missing.
    assert.isUndefined(histograms.getHistogramNamed('metric4'));
    // metric5 is not collected because
    // ":metric_begin" is missing.
    assert.isUndefined(histograms.getHistogramNamed('metric5'));
    const hist3 = histograms.getHistogramNamed('metric6');
    assert.strictEqual(hist3.numValues, 2);
    assert.strictEqual(hist3.max, 51);
    assert.strictEqual(hist3.min, 49);
    assert.strictEqual(hist3.average, 50);
    // metric7 is not collected because
    // benmark value is invalid.
    assert.isUndefined(histograms.getHistogramNamed('metric7'));
    // metric8 is not collected because
    // benchmark value is missing.
    assert.isUndefined(histograms.getHistogramNamed('metric8'));
  });

  function setUpBrowserThread(model) {
    const BROWSER_PROCESS_ID = 1234;
    const browserProcess = model.getOrCreateProcess(BROWSER_PROCESS_ID);
    const browserThread = browserProcess.getOrCreateThread(2);
    browserThread.name = 'CrBrowserMain';
    return browserThread;
  }

  function setUpRendererMainThread(model) {
    const RENDERER_PROCESS_ID = 2345;
    const rendererProcess = model.getOrCreateProcess(RENDERER_PROCESS_ID);
    const mainThread = rendererProcess.getOrCreateThread(23);
    mainThread.name = 'CrRendererMain';
    return mainThread;
  }

  function setUpOtherBrowserThread(model) {
    const BROWSER_PROCESS_ID = 1234;
    const browserProcess = model.getOrCreateProcess(BROWSER_PROCESS_ID);
    const thread = browserProcess.getOrCreateThread(3);
    thread.name = 'Other';
    return thread;
  }

  function setUpOtherRendererThread(model) {
    const RENDERER_PROCESS_ID = 2345;
    const rendererProcess = model.getOrCreateProcess(RENDERER_PROCESS_ID);
    const thread = rendererProcess.getOrCreateThread(4);
    thread.name = 'Other';
    return thread;
  }

  function addEvent(mainThread, event) {
    mainThread.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
      cat: 'blink.user_timing',
      title: event.title,
      start: event.start,
      duration: 0.0,
    }));
  }

  function addTraces(thread) {
    [
      {
        cat: 'browser',
        title: 'metric1',
        start: 1,
        duration: 1,
      },
      {
        cat: 'browser',
        title: 'metric1',
        start: 2,
        duration: 2,
      },
      {
        cat: 'browser',
        title: 'metric2',
        start: 3,
        duration: 3,
      },
      {
        cat: 'browser',
        title: 'metric3',
        start: 4,
        duration: 4,
      }
    ].forEach(slice => {
      thread.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx(slice));
    });

    [
      {
        cat: 'browser',
        title: 'metric4',
        start: 1,
        duration: 1,
      },
      {
        cat: 'browser',
        title: 'metric4',
        start: 2,
        duration: 2,
      }
    ].forEach(slice => {
      thread.asyncSliceGroup.push(tr.c.TestUtils.newAsyncSliceEx(slice));
    });
  }

  function addSingleTrace(thread) {
    const slice = {
      cat: 'browser',
      title: 'metric5',
      start: 1,
      duration: 1,
    };
    thread.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx(slice));
  }

  function setMetrics(thread) {
    const traceEvents = [
      {
        name: "metric1",
      },
      {
        name: "metric2",
        unit: "ms_biggerIsBetter",
        description: "This is metric2"
      },
      {
        name: "metric4",
      },
      {
        name: "metric5",
      },
    ]
    const report = `custom_metric:manifest:${JSON.stringify(traceEvents)}`;
    thread.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
      cat: 'blink.user_timing',
      title: report,
      start: 0,
      duration: 0,
    }));
  }

  function addPerformanceMark(thread) {
    [
      {
        title: 'metric1:metric_begin',
        start: 10,
      },
      {
        title: 'metric1:metric_end',
        start: 15,
      },
      {
        title: 'metric1:metric_begin',
        start: 17,
      },
      {
        title: 'metric1:metric_end',
        start: 19,
      },
      {
        title: 'metric2:metric_begin',
        start: 2,
      },
      {
        title: 'metric2:metric_end',
        start: 20,
      },
      {
        title: 'metric3',
        start: 25,
      },
      {
        title: 'metric4:metric_begin',
        start: 5,
      },
      {
        title: 'metric5:metric_end',
        start: 10,
      },
      {
        title: 'metric6:49:metric_value',
        start: 10,
      },
      {
        title: 'metric6:51:metric_value',
        start: 20,
      },
      {
        title: 'metric7:invalid value:metric_value',
        start: 10,
      },
      {
        title: 'metric8:metric_value',
        start: 10,
      }
    ].forEach(slice => {
      thread.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({
        cat: 'blink.user_timing',
        title: slice.title,
        start: slice.start,
        duration: 0,
      }));
    });
  }
});
</script>
